#include "EnginePrivate.h"

ConstantBufferManager GConstantBufferManager;

ConstantBufferManager::ConstantBufferManager()
{
}

ConstantBufferManager::~ConstantBufferManager()
{
	for(map<CONSTBUFFER_TYPE, ConstantBuffer*>::iterator it = m_vecConstantBuffers.begin(); it != m_vecConstantBuffers.end(); ++it)
	{
		delete it->second;
	}
	m_vecConstantBuffers.clear();
}

void ConstantBufferManager::CreateConstantBuffer()
{
	ID3D11Device* pGD3D11Device = gEngine.GetD3DDevice();
	ID3D11DeviceContext* pGD3D11Context = gEngine.GetD3DDeviceContext();

	m_vecConstantBuffers.clear();

	// CB_VS_PerBatch
	ConstantBuffer* pConstantBuffer = new ConstantBuffer;
	pConstantBuffer->m_CBType = CB_VS_PerBatch;
	pConstantBuffer->m_pBufferName = CreateString("CB_VS_PerBatch");
	pConstantBuffer->m_BufferSlot = 0;
	pConstantBuffer->m_pBufferSize = 64;
	pConstantBuffer->m_pBackBuffer = new unsigned char[pConstantBuffer->m_pBufferSize];

	D3D11_BUFFER_DESC ContantBufferDesc;
	ContantBufferDesc.ByteWidth = pConstantBuffer->m_pBufferSize;
	ContantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	ContantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	ContantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	ContantBufferDesc.MiscFlags = 0;
	ContantBufferDesc.StructureByteStride = 0;
	HRESULT hResult = pGD3D11Device->CreateBuffer(&ContantBufferDesc, NULL , &(pConstantBuffer->m_pD3D11Buffer));
	assert(hResult == S_OK && pConstantBuffer->m_pD3D11Buffer);

	ConstantValue* pConstantValue = new ConstantValue;
	pConstantValue->m_pConstantBuffer = pConstantBuffer;
	pConstantValue->m_pValueName = CreateString("WorldViewProjMatrix");
	pConstantValue->m_NameHashKey = BKDRHash(pConstantValue->m_pValueName);
	pConstantValue->m_SizeByte = 64;
	pConstantValue->m_OffsetByte = 0;
	pConstantBuffer->m_mapConstValues.insert(pair<unsigned int, ConstantValue*>(pConstantValue->m_NameHashKey, pConstantValue));

	m_vecConstantBuffers.insert(pair<CONSTBUFFER_TYPE, ConstantBuffer*>(pConstantBuffer->m_CBType, pConstantBuffer));
}

ConstantBuffer* ConstantBufferManager::GetConstantBuffer(CONSTBUFFER_TYPE CBType)
{
	map<CONSTBUFFER_TYPE, ConstantBuffer*>::iterator it = m_vecConstantBuffers.find(CBType);
	if(it != m_vecConstantBuffers.end())
		return it->second;
	else
		return 0;
}

void ConstantBufferManager::CommitCB(CONSTBUFFER_COMMIT_TYPE CommitType)
{
	switch(CommitType)
	{
		case	CB_PerBatch:
			GetConstantBuffer(CB_VS_PerBatch)->CommitCB();
			break;
	}
}

ConstantBuffer::ConstantBuffer()
{
	m_pBufferName = 0;
	m_pBackBuffer = 0;
	m_pD3D11Buffer = 0;

	m_mapConstValues.clear();

	m_bDirty = false;
}

ConstantBuffer::~ConstantBuffer()
{
	if(m_pBufferName)
		delete m_pBufferName;

	if(m_pBackBuffer)
		delete m_pBackBuffer;

	if(m_pD3D11Buffer)
		m_pD3D11Buffer->Release();

	for(map<unsigned int, ConstantValue*>::iterator it = m_mapConstValues.begin(); it != m_mapConstValues.end(); ++it)
	{
		delete it->second;
	}
	m_mapConstValues.clear();
}

ConstantValue* ConstantBuffer::GetConstantValue(char* pValueName)
{
	unsigned int NameHash = BKDRHash(pValueName);
	map<unsigned int, ConstantValue*>::iterator it = m_mapConstValues.find(NameHash);
	return it->second;
}

void ConstantBuffer::CommitCB()
{
	ID3D11DeviceContext* pGD3D11Context = gEngine.GetD3DDeviceContext();

	if(m_bDirty)
	{
		D3D11_MAPPED_SUBRESOURCE MappedSubResource;

		HRESULT hResult = pGD3D11Context->Map(m_pD3D11Buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedSubResource);

		assert(hResult == S_OK);

		unsigned char* pDst = (unsigned char*)MappedSubResource.pData;

		memcpy(pDst, m_pBackBuffer, m_pBufferSize);

		pGD3D11Context->Unmap(m_pD3D11Buffer, 0);

		m_bDirty = false;
	}

	// Set VS constant buffer
	if(m_CBType < 16)
	{
		pGD3D11Context->VSSetConstantBuffers(0, 1, &m_pD3D11Buffer);
	}

}

ConstantValue::ConstantValue()
{
	m_pValueName = 0;
}

ConstantValue::~ConstantValue()
{
	if(m_pValueName)
		delete m_pValueName;
}

void ConstantValue::SetValue(void* pInput)
{
	unsigned char* pSrc = (unsigned char*)pInput;

	unsigned char* pDst = m_pConstantBuffer->m_pBackBuffer + m_OffsetByte;
	
	unsigned int CopyBitSize = m_SizeByte;

	memcpy(pDst, pSrc, CopyBitSize);

	m_pConstantBuffer->m_bDirty = true;
}